#ifndef SYNC_H_
#define SYNC_H_

#include "../User.h"
#include "Sync_File.h"
#include "Sync_Directory.h"
#include "Sync_Regular_File.h"
#include "../FileInfo.h"

#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/filesystem.hpp>
#include "interfaces/ScanCompletedInterface.h"
#include "interfaces/SyncUpdatedInterface.h"
#include "../interfaces/TransferBundleInterface.h"
#include <queue>


using namespace std;
using boost::filesystem::path;

typedef boost::shared_ptr< list<SyncFilePtr> > SyncFileList;
typedef boost::shared_ptr< queue<SyncFilePtr> > SyncFileQueue;


class Sync : public boost::enable_shared_from_this<Sync>, public ScanCompletedInterface, public TransferBundle
{
public:
    typedef enum _SyncType
    {
        UNDEFINED_SYNC_TYPE = 0,
        PUSH_SYNC = 1,
        PULL_SYNC = 2
    } SyncType;

    typedef enum _Role
    {
        UNDEFINED_ROLE = 0,
        PEER = 1,
        SENDER = 2,
        RECEIVER = 3
    } Role;



    static string COMPOSE_TAG(string base_tag, UserPtr user);


    Sync();
    virtual ~Sync();

    /** getters / setters **/
    UserPtr get_partner() const;
    virtual string get_tag() const;
    void set_partner(UserPtr partner);
    void set_tag(string tag);
    path get_mine_path() const;
    string get_mine_path_partner_version() const;
    Sync::SyncType get_sync_type() const;
    Sync::Role get_role() const;
    path get_their_path() const;

    void set_mine_path(boost::filesystem::path minePath);
    void set_sane_name_from_path(path p);
    void set_name(path name);
    void set_sync_type_and_role(Sync::SyncType syncType, Sync::Role role);
    void set_sync_type_and_role_inverted(Sync::SyncType syncType, Sync::Role role);
    void set_their_path(boost::filesystem::path theirPath);
    bool get_receiver_accepted() const;
    void set_receiver_accepted(bool receiverAccepted);

    // returns a unique tag-username string
    string get_composed_tag() const;


    // returns a file object that we want to have transferred
//    FilePtr get_next_file();  // TODO das als Interface.. + stopp()?
    void transfers_ended_notification();


    // scans the to be synced directory in a breadth first search
    void scan();

    // pauses the sync (or so) TODO better comment
    void sleep();

    void handle_update_from_partner(SyncFileList files);

    // returns new/changed files (maximal »max«)
    SyncFileList get_updated_files(int max);


    // ScanCompletedInterface
    virtual void scan_completed_callback();
    void register_update_listener(SyncUpdatedInterface* update_listener);

    // TransferBundleInterface
//    virtual string get_tag() = 0;
    virtual shared_ptr<FileInfo> get_next_file();
    virtual shared_ptr<FileInfo> on_incoming_transfer(shared_ptr<FileInfo> file);

    virtual void on_transfer_started(shared_ptr<FileInfo> file);
    virtual void on_transfer_finished(shared_ptr<FileInfo> file);
    virtual void on_transfer_canceled(shared_ptr<FileInfo> file);

    virtual void on_enabled();
    virtual void on_disabled();
    boost::filesystem::path get_name() const;



private:
    void cache_updated_file(SyncFilePtr file);
    void notify_about_updates();

    void cache_send_file(SyncRegularFilePtr file);

    SyncFilePtr find_sync_file(FilePtr file);

private:
    string tag;  // (quasi der key)
    boost::filesystem::path name;
    UserPtr partner;
    boost::filesystem::path mine_path;
    boost::filesystem::path their_path;
    bool receiver_accepted;     // Set to true if partner accepted to receive files. (Ignore if receiving files.)
    bool direct_accept;     // XXX Ist immer momentan true. Für spätere Verwendung.. Signalisiert dem Filetransfer, Transfers ohne weitere Nachfrage zu starten.

    Sync::SyncType sync_type;
    Sync::Role role;
    // depends on sync type and role, (probably easier to check than the sync types and their properties)
    bool outgoing_transfers_enabled;
    bool incoming_transfers_enabled;

    // base directory (or file - not implemented yet)
    SyncFilePtr base_file;

    // variables for scanning base_file (breadth-first search)
    deque<SyncFilePtr> scan_queue;
    SyncFilePtr currently_scanned_file;
    list<SyncFilePtr> updated_cache;     // caches files that have a status change which have to be told to the sync-partner
    bool update_notification_pending;

    SyncUpdatedInterface* update_listener;

    deque<SyncRegularFilePtr> send_cache;    // caches files that are waiting for sending
    list<SyncRegularFilePtr> active_cache;    // caches files that are already given to the filetransfer layer  // TODO geegnete Datenstruktur?
    bool sending;
    bool sleeping;
};

typedef boost::shared_ptr<Sync> SyncPtr;

#endif /* SYNC_H_ */
